#include "cmdvalue.h"

namespace libxcom {

CmdValue::CmdValue()
{
    len = 0;
    cmd = nullptr;
}

CmdValue::CmdValue(uint8_t *cmd_, int32_t len_)
{
    construct(cmd_, len_);
}

CmdValue::CmdValue(int32_t cmd)
{
    uint8_t v = cmd;
    construct(&v, 1);
}

CmdValue::CmdValue(bool cmd)
{
    uint8_t v = cmd ? 1 : 0;
    construct(&v, 1);
}

CmdValue::CmdValue(const CmdValue &value)
{
    construct(value.Cmd(), value.Length());
}

CmdValue::~CmdValue()
{
    distruct();
}

CmdValue CmdValue::Null()
{
    return CmdValue();
}

void CmdValue::Zero()
{
    len = 0;
    cmd = nullptr;
}

bool CmdValue::IsEmpty() const
{
    return len == 0;
}

bool CmdValue::operator!=(const CmdValue &value) const
{
    return !operator==(value);
}

bool CmdValue::operator==(const CmdValue &value) const
{
    if (len == value.Length()) {
        if (len == 0)
            return true;
        return cmpdata(cmd, value.Cmd(), len);
    }
    return false;
}

void CmdValue::getNext(int32_t *next, uint8_t sub[], int32_t len)
{
    int32_t i = 0;
    int32_t j = -1;

    next[0] = -1;

    while (i < len - 1) {
        if (j == -1 || sub[i] == sub[j]) {
            ++i;
            ++j;
            next[i] = j;
        } else
            j = next[j];
    }
}

int32_t CmdValue::Contains(const CmdValue &value)
{
    int32_t ipos, jpos;
    int32_t *next = nullptr;

    if (len < value.Length() || value.Length() == 0)
        return -1;
    next = (int32_t *) malloc(value.Length() * sizeof(int32_t));
    getNext(next, value.Cmd(), value.Length());
    ipos = 0;
    jpos = 0;
    while (ipos < len && jpos < value.Length()) {
        if (jpos == -1 || cmd[ipos] == value[jpos]) {
            ipos++;
            jpos++;
        } else
            jpos = next[jpos];
    }
    free(next);

    if (jpos == value.Length())
        return ipos;
    return -1;
}

void CmdValue::Show(const std::string &desc)
{
    if (desc == "") {
        HTELINK_LOG_INFO("len = %d,cmd: %s", len, toString().c_str());
    } else {
        HTELINK_LOG_INFO("%s: %s", desc.c_str(), toString().c_str());
    }
}

std::string CmdValue::toString()
{
    std::string str;
    for (int i = 0; i < len; i++) {
        char buf[10];
        sprintf(buf, "%02X ", cmd[i]);
        str += buf;
    }
    return str;
}

uint8_t *CmdValue::Cmd() const
{
    return cmd;
}

int32_t CmdValue::Length() const
{
    return len;
}

uint8_t *CmdValue::CmdAt(int32_t pos) const
{
    ASSERT((pos >= 0) && (pos < len));
    return &cmd[pos];
}

uint8_t &CmdValue::operator[](int32_t pos) const
{
    ASSERT((pos >= 0) && (pos < len));
    return cmd[pos];
}

CmdValue &CmdValue::operator=(const CmdValue &value)
{
    distruct();
    construct(value.Cmd(), value.Length());
    return *this;
}

CmdValue &CmdValue::Insert(int32_t index, const uint8_t value)
{
    if (cmd) {
        int32_t len_ = len;
        uint8_t *newcmd = new uint8_t[len_ + 1];
        if (index > 0)
            copydata(&newcmd[0], cmd, index);
        newcmd[index] = value;
        copydata(&newcmd[index + 1], &cmd[index], len_ - index);
        distruct();
        cmd = newcmd;
        len = len_ + 1;
    } else {
        cmd = new uint8_t[1];
        cmd[0] = value;
        len = 1;
    }
    return *this;
}

CmdValue &CmdValue::operator+=(const CmdValue &value)
{
    if (value == Null())
        return *this;

    if (cmd) {
        int32_t len_ = len;
        uint8_t *newcmd = new uint8_t[len_ + value.Length()];
        copydata(newcmd, cmd, len_);
        copydata(&newcmd[len_], value.Cmd(), value.Length());
        distruct();
        cmd = newcmd;
        len = len_ + value.Length();
    } else {
        cmd = new uint8_t[value.Length()];
        copydata(cmd, value.Cmd(), value.Length());
        len = value.Length();
    }

    return *this;
}

CmdValue &CmdValue::operator+=(const uint8_t value)
{
    if (cmd) {
        int32_t len_ = len;
        uint8_t *newcmd = new uint8_t[len_ + 1];
        copydata(newcmd, cmd, len_);
        copydata(&newcmd[len_], &value, 1);
        distruct();
        cmd = newcmd;
        len = len_ + 1;
    } else {
        cmd = new uint8_t[1];
        cmd[0] = value;
        len = 1;
    }
    return *this;
}

CmdValue &CmdValue::operator-=(const CmdValue &value)
{
    if (value == Null() || !cmd)
        return *this;

    int32_t pos = Contains(value);
    return TrimLeft(pos);
}

CmdValue &CmdValue::operator-=(const uint8_t value)
{
    if (!cmd)
        return *this;

    if (cmd[0] == value)
        return TrimLeft(1);
    return *this;
}

CmdValue &CmdValue::operator^(const CmdValue &value)
{
    if (len != value.len) {
        HTELINK_LOG_ERR("length:%d not equal %d", len, value.len);
        return *this;
    }
    for (int i = 0; i < len; i++) {
        cmd[i] ^= value.cmd[i];
    }
    return *this;
}

CmdValue &CmdValue::operator^(const uint8_t ch)
{
    if (len > 0) {
        HTELINK_LOG_ERR("length equal %d", len);
        return *this;
    }
    for (int i = 0; i < len; i++) {
        cmd[i] ^= ch;
    }
    return *this;
}

CmdValue &CmdValue::TrimLeft(int32_t length)
{
    if (length <= 0)
        return *this;
    if (length >= len) {
        distruct();
        return *this;
    }
    copydata(cmd, &cmd[length], len - length);
    len -= length;
    return *this;
}

inline void CmdValue::copydata(uint8_t *dst, const uint8_t *src, int32_t len)
{
    for (int i = 0; i < len; i++) {
        dst[i] = src[i];
    }
}

bool CmdValue::cmpdata(const uint8_t *dst, const uint8_t *src, int32_t len) const
{
    for (int i = 0; i < len; i++) {
        if (dst[i] != src[i])
            return false;
    }
    return true;
}

inline void CmdValue::construct(const uint8_t *cmd_, int32_t len_)
{
    len = len_;
    if (len_ <= 0) {
        cmd = nullptr;
        return;
    }
    cmd = new uint8_t[len];
    copydata(cmd, cmd_, len);
}

inline void CmdValue::distruct()
{
    if (cmd && len > 0)
        delete[] cmd;
    cmd = nullptr;
    len = 0;
}
} // namespace libxcom
